home *** CD-ROM | disk | FTP | other *** search
/ Magnum One / Magnum One (Mid-American Digital) (Disc Manufacturing).iso / d12 / vol7n6.arc / CALC.ASM < prev    next >
Assembly Source File  |  1988-03-01  |  64KB  |  1,213 lines

  1. ;Calc for the IBM Personal Computer - 1987 by Douglas Boling
  2. bios_data     segment at 40h                ;BIOS data area
  3.               org    17h
  4. bios_kbd_stat db ?                          ;keyboard status byte
  5.               org    4ah
  6. bios_crt_col  dw ?                          ;number of columns on the screen
  7.               org    63h
  8. addr_6845     dw ?                          ;6845 Index Register address
  9.               org    84h
  10. bios_crt_row  db ?                          ;number of rows
  11. bios_data     ends
  12.  
  13. code          segment para public 'code'
  14.               assume cs:code
  15.               org 100h
  16. entry:        jmp initialize                ;jump to initialization code
  17. program       db "CALC 1.0 (c) 1988 Ziff Communications Co.",13,10
  18.               db "PC Magazine ",254," Douglas Boling",13,10
  19.               db "Hot Key is Alt-S",13,10,"$",1ah
  20. adapter            db 2                     ;0 = CGA, 1 = MDA, 2 = EGA
  21. num_vid_col        dw ?                     ;number of columns on screen
  22. num_vid_rows       db ?                     ;number of columns on the screen
  23. v_segment          dw ?                     ;video segment address
  24. v_page             db ?                     ;current video page
  25. border_attr        db ?                     ;window border attribute
  26. text_attr          db ?                     ;window text attribute
  27. header_attr        db ?                     ;window header attribute
  28. window_row         db 3                     ;row of left corner of window
  29. window_column      db 10                    ;column of left corner of window
  30. column_adj         dw ?                     ;screen adjust for index registers
  31. active             db 0                     ;status of interrupt routine
  32. old_cursor_pos     dw ?                     ;row and column of screen cursor
  33. my_cursor_pos      dw ?                     ;cursor position
  34. ;
  35. base_flag          dw 10                    ;numeric base
  36. pending_op         dw 3dh                   ;pending operation (3d = nop)
  37. fixed_flag         db 0                     ;indicates fixed math mode
  38. decimal_flag       db 0                     ;flag for decimal point entry
  39. entry_pres         db 0                     ;entry reg nonempty flag
  40. ;
  41. entry_reg_high     dw 0                     ;most significant 16 bits
  42. entry_reg_low      dw 0                     ;least sig. 16 bits for entry num
  43. ;
  44. result_reg_high    dw 0                     ;most sig. 16 bits of result reg
  45. result_reg_low     dw 0                     ;least sig. 16 bits
  46. ;
  47. sign               db 0
  48. base_ptr:          db "Hex     Binary  Octal   Decimal Fixed   "
  49. number_label       db "hbod"
  50. op_table:          db " and or  xor err"
  51. ;
  52. old_kbd_status     db ?
  53. old_int_9h         label dword              ;old interrupt vector
  54. old_keyboard_int   dw 2 dup (?)
  55. screen_buffer      dw offset initialize     ;pointer to screen buffer area
  56. ;
  57. mono_values        dw 0b000h                ;segment
  58.                    db 70h                   ;border
  59.                    db 07h                   ;text
  60.                    db 07h                   ;header
  61. color_values       dw 0b800h                ;segment
  62.                    db 0fh                   ;border
  63.                    db 1fh                   ;text
  64.                    db 1eh                   ;header
  65. enable_values      db 2Ch,28h,2Dh,29h       ;values to enable CGA display
  66.                    db 2Ah,2Eh,1Eh
  67. header_text        db "Calculator   Base:              Esc = Quit"
  68. help_text1:        db "f1 base   f3 and  f5 xor   f7 sal  f9  +/-"
  69. help_text2:        db "f2 fixed  f4 or   f6 not   f8 sar  f10 clr"
  70. ;-----------------------------------------------------------------------------
  71. ;Front-end routine for the keyboard interrupt handler.  Execution is vectored
  72. ;here whenever an interrupt 9 is generated by the PC keyboard.
  73. ;-----------------------------------------------------------------------------
  74. main          proc near
  75.               assume cs:code,ds:nothing,es:nothing,ss:nothing
  76.               pushf
  77.               cmp    cs:active,0            ;Calc currently active?
  78.               jne    quick_out              ;yes, Exit.
  79.               sti                           ;no, start initialization, first
  80.               push   ax                     ;  enable interrupts and save
  81.               push   bx                     ;  registers.
  82.               push   cx
  83.               push   dx
  84.               push   si
  85.               push   di
  86.               push   ds
  87.               push   es
  88.               push   bp
  89.               in     al,60h                 ;get scan code from keyboard
  90.               cmp    al,31                  ;check for 's' key
  91.               jne    out1                   ;no, then exit.
  92.               mov    ah,2                   ;check shift keys
  93.               int    16h
  94.               and    al,0fh
  95.               cmp    al,8                   ;check alt key
  96.               je     main1                  ;alt key pressed, pop up.
  97. out1:         pop    bp
  98.               pop    es                     ;Exit, first restore registers,
  99.               pop    ds                     ;  then jump using the old
  100.               pop    di                     ;  interrupt vector that was
  101.               pop    si                     ;  replaced.
  102.               pop    dx
  103.               pop    cx
  104.               pop    bx
  105.               pop    ax
  106. quick_out:    popf
  107.               jmp    cs:old_int_9h
  108. ;  Start of 'real' code. Clean up interrupt state and check video mode.
  109. main1:        call kb_reset                 ;The hot key combination has been
  110.               push   cs                     ;  pressed, spring into action by
  111.               pop    ds                     ;  resetting the keyboard
  112.               assume ds:code                ;  interrupt, and setting the
  113.               mov    ax,bios_data           ;  data segment register.
  114.               mov    es,ax                  ;Set es for bios segment. Then
  115.               assume es:bios_data           ;  grab significant data.
  116.               mov    ax,es:bios_crt_col     ;save the number of columns
  117.               mov    num_vid_col,ax
  118.               mov    al,es:bios_crt_row     ;save the number of rows
  119.               mov    num_vid_rows,al
  120.               mov    al,es:bios_kbd_stat    ;save the state of the keyboard
  121.               mov    old_kbd_status,al
  122.               or     es:bios_kbd_stat,20h   ;set num-lock on
  123.               mov    ah,12h                 ;Check for EGA by testing video
  124.               mov    bl,10h                 ;  function 12h. If bl returns
  125.               int    10h                    ;  unchanged, then EGA is not
  126.               cmp    bl,10h                 ;  present.
  127.               jne    main110
  128.               mov    adapter,0              ;not EGA, assume CGA
  129.               mov    num_vid_rows,24        ;No ega must be 25 rows.
  130.               mov    ax,es:addr_6845
  131.               test   al,40h
  132.               jnz    main110                ;if the bit is 1 then its a MDA
  133.               inc    adapter
  134. main110:      push   cs                     ;Set es to same segment as code.
  135.               pop    es
  136.               assume es:code
  137.               mov    ah,15                  ;Get current video mode.
  138.               int    10h
  139.               cmp    al,3                   ;is current video mode 0 - 3 ?
  140.               jle    main2                  ;yes, then branch
  141.               cmp    al,7                   ;is current video mode 7?
  142.               je     main111                ;yes, then branch. else, exit.
  143. done:                                       ; Restore the state of the keyboard
  144.               mov    al,old_kbd_status      ;get old key status
  145.               and    al,20h                 ;isolate numlock
  146.               cmp    al,0                   ;was numlock on or off?
  147.               jnz    done_1                 ;it was on - no change needed
  148.               mov    bx,bios_data
  149.               mov    ds,bx
  150.               assume ds:bios_data
  151.               and    ds:bios_kbd_stat,not 20h  ;write to keyboard flags byte
  152. done_1:       pop    bp
  153.               pop    es                     ;Exit with an interrupt return.
  154.               pop    ds
  155.               pop    di
  156.               pop    si
  157.               pop    dx
  158.               pop    cx
  159.               pop    bx
  160.               pop    ax
  161.               popf
  162.               iret
  163.               assume cs:code, ds:code, es:code
  164. main111:      mov    di,offset mono_values  ;set monochrome attributes
  165.               jmp    short main3
  166. main2:        mov    di,offset color_values ;set color attributes
  167. main3:        mov    ax,[di]
  168.               mov    v_segment,ax           ;set video segment and attributes
  169.               mov    ax,2[di]
  170.               mov    border_attr,al
  171.               mov    text_attr,ah
  172.               mov    al,4[di]
  173.               mov    header_attr,al
  174.               mov    ax,num_vid_col         ;Compute the column adjustment
  175.               cmp    ax,80                  ;  value from the number of
  176.               jl     done                   ;  columns on the screen. If the
  177.               sal    ax,1                   ;  number of columns in the
  178.               sub    al,88                  ;  screen is < 80, don't pop up.
  179.               mov    column_adj,ax
  180.               mov    v_page,bh              ;Save video page.
  181.               mov    ah,3                   ;Get cursor position, save it,
  182.               int    10h                    ;  then position the cursor to
  183.               mov    old_cursor_pos,dx      ;  one row below the screen so
  184.               mov    dh,num_vid_rows        ;  that it will be hidden.
  185.               add    dh,2
  186.               mov    dl,0
  187.               mov    ah,2
  188.               int    10h
  189.               mov    active,1               ;indicate calc is active.
  190.               call   set_cursor
  191.               mov    di,screen_buffer       ;Save the screen where the
  192.               xor    ax,ax                  ;  window will be. Then, pop up
  193.               call   screen_ops             ;  the window.
  194.               mov    bl,0                   ;Display the contents of the
  195.               cmp    entry_pres,0           ;  of the entry register if
  196.               je     main32                 ;  something is in it, else
  197.               inc    bl                     ;  display the result register.
  198. main32:       call   display_reg
  199. ;The window is now displayed on the screen.  Wait for a keypress.
  200. main4:        mov    dx,my_cursor_pos       ;Get the position of my 'cursor'.
  201.               mov    al,"<"                 ;  then write it to the screen.
  202.               mov    ah,header_attr
  203.               call   output_char
  204.               call   display_base           ;indicate dec, hex, oct, bin, fix
  205. main41:       mov    ah,0                   ;get a keypress by first executing
  206.               int    28h                    ;  a dos idle loop, to let other
  207.               mov    ah,1                   ;  programs have a chance, then
  208.               int    16h                    ;  checking for a key before
  209.               jz     main41                 ;  actually getting the key. If
  210.               mov    ah,0                   ;  no key, idle again.
  211.               int    16h
  212. main5:        cmp    al,27                  ;ESC key pressed?
  213.               je     end_it                 ;yes, clean up and exit
  214.               cmp    al,08                  ;is it a backspace
  215.               jne    main6
  216.               call   back_space             ;yes, process backspace
  217.               jmp    short main4
  218. main6:        cmp    ah,93                  ;is it shift f10 ?
  219.               je     main7                  ;yes, jump to clear routine.
  220.               cmp    ah,59                  ;is it f1 ?
  221.               je     main9                  ;yes, change base
  222.               cmp    ah,60                  ;is it f2 ?
  223.               je     main9                  ;yes, change base
  224.               cmp    ah,64                  ;is it between f6 and f10 ?
  225.               jl     main8                  ;  If so, call unary operator
  226.               cmp    ah,68                  ;  routine
  227.               jg     main8
  228. main7:        call   un_proc
  229.               jmp    short main4
  230. main8:        call   key_proc
  231.               jmp    short main4
  232. main9:        call   base_chang             ;f1 or f2, change base
  233.               jmp    short main4            ;goto end
  234. ;  Escape key has been pressed, clean up and exit.
  235. end_it:       mov    di,screen_buffer       ;point DI to holding buffer
  236.               mov    al,1
  237.               call   screen_ops             ;restore video memory contents
  238.               mov    dx,old_cursor_pos      ;Restore the cursor to the
  239.               mov    bh,v_page              ;  position it was before calc
  240.               mov    ah,2                   ;  was called.
  241.               int    10h
  242.               mov    active,0               ;reset status flag
  243.               jmp    done                   ;exit
  244. main          endp
  245. ;-----------------------------------------------------------------------------
  246. ; process keys - Process a all keys but esc, f1, f2, and f6 - f10
  247. ;-----------------------------------------------------------------------------
  248. key_proc      proc   near
  249.               cmp    al,0                   ;see if extended key
  250.               jne    key0
  251.               cmp    ah,61                  ;if extended, allow only
  252.               jl     key_end                ;  keys f3 - f10
  253.               cmp    ah,68
  254.               jg     key_end
  255.               jmp    short key_oper
  256. key0:         cmp    al,30h                 ;is it lower than 0
  257.               jl     key2                   ;yes, check for other functions
  258.               mov    bx,base_flag           ;get base
  259.               cmp    bl,10h                 ;see if in hex
  260.               je     key1                   ;if so goto hex mode checking
  261.               or     bl,30h                 ;convert to ascii
  262.               cmp    al,bl                  ;is number less then the base?
  263.               jl     key_num                ;yes, goto number processing
  264.               jmp    short key2             ;no, check for other characters
  265. ;Check for a-f if in hex mode
  266. key1:         cmp    al,39h                 ;see if number less than 10
  267.               jle    key_num
  268.               mov    bl,al                  ;copy ascii character
  269.               and    bl,0dfh                ;make lower and upper case same
  270.               cmp    bl,41h                 ;is it lower than a ?
  271.               jl     key2                   ;yes, see if other character
  272.               cmp    bl,46h                 ;no, is it greater then an f ?
  273.               jg     key2                   ;yes, check for other characters
  274. key_num:      call   number_key             ;no, it must be a number
  275.               jmp    short key_end             ;get another keypress
  276. ;Check for math functions
  277. key2:         cmp    al,2ah                 ;if ascii code between 2a and
  278.               jl     key3                   ;2f then its a *+,-. or /
  279.               cmp    al,2fh
  280.               jg     key3
  281.               cmp    al,","                 ;see if its a comma that
  282.               je     key_end                   ;  slipped through, if so ret.
  283.               cmp    al,"."                 ;is it a decimal point?
  284.               jne    key_oper
  285.               mov    decimal_flag,1         ;set flag
  286.               jmp    short key_end
  287. key3:         cmp    al,"%"                 ;see if other mod operator
  288.               je     key_oper
  289.               cmp    al,13                  ;see if enter was pressed
  290.               je     key4                   ;yes, process enter
  291.               cmp    al,"="                 ;see if = was pressed
  292.               jne    key5                   ;no, check for other keys
  293. key4:         mov    al,"="                 ;yes, clear enter code
  294.               call   oper_proc              ;complete last math operation
  295.               mov    bl,0                   ;display result register
  296.               call   display_reg            ;call display register
  297.               jmp    short key_end          ;get a keypress
  298. key5:         cmp    al,"\"                 ;see if mod operation
  299.               jne    key_end
  300. key_oper:     call   oper_proc              ;call the math procedure
  301. key_end:      ret                           ;end
  302. key_proc      endp
  303. ;-----------------------------------------------------------------------------
  304. ; process base changes - Change the value in the base flag, then display reg.
  305. ;-----------------------------------------------------------------------------
  306. base_chang    proc   near
  307.               cmp    ah,60                  ;see if changing to fixed
  308.               je     base_fixed
  309.               cmp    fixed_flag,1           ;if in fixed mode, only change
  310.               je     base_fixed             ;  to decimal
  311.               mov    ax,base_flag           ;Get the base flag, add 6 and
  312.               add    al,6                   ;  remove the 3rd bit, what you
  313.               and    al,0bh                 ;  have is the new base except for
  314.               jnz    base_c1                ;  16 witch can be added to the
  315.               add    al,16                  ;  zero result for hex.
  316. base_c1:      mov    base_flag,ax
  317.               jmp    short base_end
  318. base_fixed:   mov    base_flag,10           ;Make sure base = 10 then
  319.               cmp    fixed_flag,0           ;  if already in fixed, make
  320.               je     base_fixed1            ;  integer by dividing by 100.
  321.               mov    fixed_flag,0           ;  If in integer decimal, make
  322.               mov    decimal_flag,0         ;  room for the fraction by
  323.               mov    ch,01                  ;  multiplying by 100.
  324.               mov    di,offset result_reg_high
  325.               call   fixed_adjust
  326.               mov    di,offset entry_reg_high
  327.               call   fixed_adjust
  328.               jmp    short base_end
  329. base_fixed1:  inc    fixed_flag             ;set fixed flag
  330.               xor    cx,cx
  331.               mov    di,offset result_reg_high
  332.               call   fixed_adjust
  333.               mov    di,offset entry_reg_high
  334.               call   fixed_adjust
  335. base_end:     mov    bl,0                   ;display result register unless
  336.               cmp    entry_pres,0           ;  there is a number in the
  337.               je     base_end1              ;  entry register.
  338.               inc    bl                     ;change register flag for the
  339. base_end1:    call   display_reg            ;  display routine.
  340.               ret
  341. base_chang    endp
  342. ;-----------------------------------------------------------------------------
  343. ; mulitply and divide registers by 100
  344. ; entry: ch = 1, divide.  ch = 0, multiply.
  345. ;-----------------------------------------------------------------------------
  346. fixed_adjust  proc   near                   ;Moving from fixed to decimal
  347.               push   bx                     ;  and back again needs an easy
  348.               mov    bx,100                 ;  way to multiply and divide by
  349.               call   mul_div_shrt           ;  100.
  350.               pop    bx
  351.               ret                           ;end
  352. fixed_adjust  endp
  353. ;-----------------------------------------------------------------------------
  354. ; mulitply and divide registers by bx
  355. ; entry: ch = 1, divide.  ch = 0, multiply.
  356. ;-----------------------------------------------------------------------------
  357. mul_div_shrt  proc   near                   ;Moving from fixed to decimal
  358.               mov    cl,sign
  359.               push   cx
  360.               mov    sign,0                 ;  and back again needs an easy
  361.                                             ;  way to multiply and divide by
  362.               cmp    base_flag,10           ;  100. The math routines also
  363.               jne    adj3                   ;  need this routine to correct
  364.               cmp    word ptr [di],0        ;  the decimal point on multiplys
  365.               jge    adj3                   ;  and divides.
  366.               call   negate_reg
  367.               inc    sign                   ;If the register we are converting
  368. adj3:         mov    ax,[di]                ;  is negitive, change its sign
  369.                                             ;  by negating it.
  370.               cmp    ch,0                   ;For divide, get the high word,
  371.               je     adj4                   ;  convert it to a double word,
  372.               xor    dx,dx                  ;  perform the first divide. Save
  373.               div    bx                     ;  the result, and use the
  374.               mov    [di],ax                ;  remainder as the upper word
  375.               mov    ax,2[di]
  376.               div    bx                     ;  of the lower word divide.
  377.               jmp    short adj5
  378. adj4:         mul    bx                     ;For multiply, multiply the high
  379.               mov    [di],ax                ;  word, then the low, and add the
  380.               mov    ax,2[di]               ;  upper 16 bits of the second
  381.               mul    bx                     ;  product to the first product.
  382.               add    [di],dx
  383. adj5:         mov    2[di],ax               ;At the end recall if the register
  384.               cmp    sign,0                 ;  as negitive on entry. If so,
  385.               je     adj6                   ;  negate the result to return
  386.               call   negate_reg             ;  the register to its orginal
  387. adj6:         pop    cx                     ;  sign.
  388.               mov    sign,cl
  389.               ret
  390. mul_div_shrt  endp
  391. ;-----------------------------------------------------------------------------
  392. ;process unary operations.
  393. ;-----------------------------------------------------------------------------
  394. un_proc       proc   near
  395.               cmp    entry_pres,0           ;set up di to point to the
  396.               je     un1                    ;  proper register.
  397.               mov    di,offset entry_reg_high
  398.               jmp    short un2
  399. un1:          mov    di,offset result_reg_high
  400. un2:          cmp    ah,64                  ;is it f6 ?
  401.               jne    un3                    ;no, check for other keys
  402.               not    word ptr [di]          ;not register
  403.               not    word ptr 2[di]
  404.               jmp    short un_end
  405. un3:          cmp    ah,65                  ;is it f7, left shift ?
  406.               jne    un4
  407.               mov    bl,0
  408.               jmp    short un31
  409. un4:          cmp    ah,66                  ;is it f8, right shift ?
  410.               jne    un5
  411.               mov    bl,1
  412. un31:         call   shift_reg
  413.               jmp    short un_end
  414. un5:          cmp    ah,68                  ;is it f10 ?
  415.               jl     un6                    ;no, check for other keys
  416.               xor    dx,dx
  417.               mov    di,offset entry_reg_high
  418.               mov    [di],dx                ;Clear both registers
  419.               mov    2[di],dx
  420.               mov    4[di],dx
  421.               mov    6[di],dx
  422.               cmp    ah,93                  ;do we clear the screen?
  423.               jne    un_end
  424.               mov    cx,7                   ;yes, do so by scrolling the
  425. un50:         call   scroll_window          ;  window up 7 lines.
  426.               loop   un50
  427.               jmp    short un_end
  428. un6:          cmp    ah,67                  ;is it f9 ?
  429.               jne    un_end                 ;no, check for other keys
  430.               call   negate_reg             ;  negate the result register.
  431. un_end:       mov    bl,entry_pres
  432.               call   display_reg            ;display register
  433.               ret
  434. un_proc       endp
  435. ;-----------------------------------------------------------------------------
  436. ;process numbers - insert digits from the keyboard into the entry register.
  437. ;-----------------------------------------------------------------------------
  438. number_key    proc   near
  439.               cmp    al,39h                 ;see if a number not a - f
  440.               jle    num1                   ;yes, skip letter conversion
  441.               add    al,9                   ;convert letters to hex values
  442. num1:         and    ax,000fh               ;strip off ascii code
  443.               push   ax                     ;save digit
  444.               mov    sign,ah                ;ah = 0, clear sign flag
  445.               mov    di,offset entry_reg_high
  446.               cmp    base_flag,10
  447.               jne    num10
  448.               cmp    word ptr [di],0        ;Check if the entry register is
  449.               jge    num10                  ;  negitive, if so, negate it.
  450.               call   negate_reg
  451.               inc    sign
  452. num10:        pop    ax                     ;get the digit back
  453.               xor    dx,dx
  454.               cmp    fixed_flag,0           ;See if in fixed mode.
  455.               je     num2
  456.               mov    cl,decimal_flag
  457.               cmp    cl,2                   ;If were in the fix point mode,
  458.               je     num11                  ;  multiply all digits by 100
  459.               jg     num20                  ;  until the decimal flag has
  460.               mov    bx,10                  ;  been set. Then accept two
  461.               mul    bx                     ;  fraction digits shifting only
  462.               cmp    cl,1                   ;  the tenths digit.
  463.               je     num11                  ;  entered.
  464.               mul    bx
  465. num11:        cmp    cl,0
  466.               je     num2
  467.               inc    decimal_flag
  468.               mov    bx,2[di]
  469.               jmp    short num4
  470. num2:         mov    bx,ax
  471.               mov    ax,[di]                ;get upper 16 bits
  472.               cmp    base_flag,10
  473.               je     num210
  474.               mul    base_flag
  475.               jmp    short num211
  476. num210:       imul   base_flag              ;make room for the new digit
  477. num211:       jc     num21
  478.               mov    [di],ax                ;return high word
  479.               mov    ax,2[di]               ;get low 16 bits
  480.               mul    base_flag              ;repeat shift to make room
  481. num4:         add    ax,bx                  ;add in new digit
  482.               adc    dx,0
  483.               add    [di],dx                ;add overflow to the high 16 bits
  484. num42:        mov    2[di],ax               ;store lower 16 bits
  485. ; if a new number, scroll window before displaying
  486.               cmp    sign,0                 ;Before displaying, see if the
  487.               je     num20                  ;  register was orginally negitive
  488.               call   negate_reg             ;  if so, negate it.
  489. num20:        cmp    entry_pres,0           ;Check for a number in the entry
  490.               jne    num21                  ;  register or a pending operation
  491.               mov    bx,pending_op
  492.               cmp    bl,"="
  493.               jne    num21
  494.               call   scroll_window
  495. num21:        mov    bl,1                   ;display entry register
  496.               call   display_reg
  497.               inc    entry_pres             ;entry reg contains a number
  498. num3:         ret
  499. number_key    endp
  500. ;-----------------------------------------------------------------------------
  501. ;write_at_cursor - keeps track of the cursor and calls output character
  502. ;Entry:  al - ascii value of characher to be displayed
  503. ;-----------------------------------------------------------------------------
  504. write_at_cur  proc near
  505.               push   ax                     ;save registers ax and dx
  506.               push   dx
  507.               mov    dx,my_cursor_pos       ;get cursor position
  508.               mov    ah,text_attr           ;use proper display attribute
  509.               call   output_char            ;else, display character
  510.               inc    dl                     ;move cursor over
  511.               mov    my_cursor_pos,dx       ;store new cursor position
  512. wac1:         pop    dx
  513.               pop    ax                     ;same with ax
  514.               ret                           ;done.
  515. write_at_cur  endp
  516. ;-----------------------------------------------------------------------------
  517. ;oper_proc - processes all operators for the program
  518. ;Entry:  ax - non-numeric keystroke
  519. ;-----------------------------------------------------------------------------
  520. oper_proc     proc near
  521.               push   ax                     ;save the key
  522.               cmp    entry_pres,0           ;is there a number in the ent reg?
  523.               jne    oper1                  ;yes, perform operation
  524.               mov    pending_op,ax          ;no, save operation and exit
  525.               xor    cx,cx
  526.               jmp    short operend
  527. oper1:        xchg   pending_op,ax          ;get pending op and save new op
  528.               call   math_proc              ;process math function
  529. operend:      pop    dx
  530.               cmp    cx,0
  531.               je     oper4
  532.               mov    dx,cx
  533. oper4:        cmp    dl,0                   ;see if extended code
  534.               je     operlog
  535.               mov    al," "                 ;print space to seperate operator
  536.               call   write_at_cur
  537.               mov    ax,dx                  ;get pending operation
  538.               call   write_at_cur           ;display operator
  539. oper5:        call   scroll_window
  540. operend1:     xor    bx,bx                  ;clear bx
  541.               mov    entry_reg_high,bx      ;clear entry register
  542.               mov    entry_reg_low,bx
  543.               mov    decimal_flag,bl
  544.               mov    entry_pres,bl
  545. operexit:     ret                           ;done.
  546. operlog:      mov    cx,dx
  547.               sub    ch,61                  ;convert keycode into offset
  548.               sal    ch,1
  549.               sal    ch,1
  550.               mov    bx,offset op_table
  551.               add    bl,ch                  ;point to correct label
  552.               mov    cx,4                   ;display a space and 3 characters
  553. operlog1:     mov    al,[bx]                ;get a character to display
  554.               call   write_at_cur
  555.               inc    bx
  556.               loop   operlog1
  557.               jmp    short oper5
  558. oper_proc     endp
  559. ;-----------------------------------------------------------------------------
  560. ;math_proc - processes all math for the program
  561. ;Entry:  al - ascii value of the operation or 0 for result reg = entry reg
  562. ;-----------------------------------------------------------------------------
  563. math_proc     proc near
  564.               mov    bx,ax                  ;copy key press
  565.               mov    dx,entry_reg_high      ;get result register
  566.               mov    ax,entry_reg_low       ;
  567.               mov    di,offset result_reg_high
  568.               xor    cx,cx                  ;clear error flag
  569.               cmp    bx,0
  570.               je     math02
  571.               cmp    bl,0
  572.               jne    math01
  573.               jmp    short logic_start
  574. math01:       cmp    bl,"+"                 ;see if addition
  575.               je     math1
  576.               cmp    bl,"-"                 ;see if subtraction
  577.               je     math2
  578.               cmp    bl,"*"                 ;see if multiplication
  579.               je     mathmd
  580.               cmp    bl,"/"                 ;see if division
  581.               je     mathmd
  582.               cmp    bl,"%"
  583.               je     mathmd
  584.               cmp    bl,"\"                 ;see if mod operation
  585.               je     mathmd
  586. math02:       mov    [di],dx                ;If operator not recognized,
  587.               mov    2[di],ax               ;  copy entry register to
  588.               jmp    short mathend
  589. math1:        add    2[di],ax               ;add
  590.               adc    [di],dx
  591.               jo     matherr
  592. math11:       jmp    short mathend
  593. math2:        sub    2[di],ax               ;subtract
  594.               sbb    [di],dx
  595.               jo     matherr                ;jump to error if underflow
  596.               jmp    short mathend
  597. mathmd:       call   mul_div
  598. mathend:      ret
  599. matherr:      mov    cx,4000h               ;point to error tag
  600.               ret
  601. logic_start:  cmp    bh,61                  ;see if and operation
  602.               jne    logic1                 ;no, look some more
  603.               and    [di],dx                ;perform and
  604.               and    2[di],ax
  605.               jmp    short logic_end        ;print op and end
  606. logic1:       cmp    bh,62                  ;see if or operation
  607.               jne    logic2                 ;no, look some more
  608.               or     [di],dx                ;do or
  609.               or     2[di],ax
  610.               jmp    short logic_end        ;print op and end
  611. logic2:       cmp    bh,63                  ;see if xor operation
  612.               jne    logic_end              ;give up looking
  613.               xor    [di],dx                ;do xor
  614.               xor    2[di],ax
  615. logic_end:    xor    cx,cx                  ;clear error code
  616.               ret
  617. math_proc     endp
  618. ;-----------------------------------------------------------------------------
  619. ;Multiply and Divide - process multiply and divide math functions.
  620. ;-----------------------------------------------------------------------------
  621. mul_div       proc   near
  622.               xor    cx,cx                  ;zero cl
  623.               mov    sign,cl                ;zero sign flag
  624.               cmp    word ptr [di],cx       ;see if result reg is negative
  625.               jge    md1                    ;no, skip negation
  626.               call   negate_reg
  627.               inc    sign                   ;set 1 negative number
  628. md1:          mov    si,offset entry_reg_high
  629.               cmp    word ptr [si],0        ;see if negative
  630.               jge    md2                    ;no, skip negation
  631.               push   di
  632.               mov    di,si                  ;neg entry register
  633.               call   negate_reg
  634.               pop    di
  635.               inc    sign                   ;set 1 more negative number
  636. ; 2 positive numbers, now is mul or div ?
  637. md2:          push   bx                     ;save the operation
  638.               cmp    bl,"*"                 ;if not '*' it must be divide
  639.               jne    md6                    ;  or mod operation.
  640. md3:          mov    ax,[di]                ;get result high
  641.               mul    word ptr [si]          ;multiply by entry high
  642.               mov    cx,dx
  643.               add    cx,ax
  644.               mov    ax,2[di]               ;get result low
  645.               mul    word ptr [si]          ;multiply by entry high
  646.               add    cx,dx
  647.               mov    bx,ax                  ;start high word of product
  648.               mov    ax,[di]                ;get result high
  649.               mul    word ptr 2[si]         ;multiply by entry low
  650.               add    cx,dx
  651.               add    bx,ax                  ;continue to build final product
  652.               mov    ax,2[di]               ;get result low
  653.               mul    word ptr 2[si]         ;multiply by entry low
  654.               add    bx,dx                  ;complete upper 16 bits
  655.               cmp    cx,0                   ;see if any overflow
  656.               jne    mderr
  657.               cmp    base_flag,10           ;If in base 10 protect the sign
  658.               jne    md55                   ;  bit by declaring an overflow
  659.               cmp    bx,0                   ;  if it has been changed.
  660.               jl     mderr
  661. md55:         pop    cx                     ;get the operation back
  662.               jmp    short mdend
  663. ;Division part of the routine.
  664. md6:          cmp    word ptr [si],0
  665.               jne    md61
  666.               cmp    word ptr 2[si],0       ;see if entry reg = 0
  667.               je     mderr                  ;  if so, indicate error.
  668. md61:         cmp    fixed_flag,0           ;If in fixed mode multiply the
  669.               je     md7                    ;  dividend by 100 to make room
  670.               mov    ch,0                   ;  for the fraction.
  671.               call   fixed_adjust
  672. md7:          cmp    word ptr [si],0        ;Now check to see that the
  673.               je     md8                    ;  divisor is not too big.
  674.               mov    bl,1                   ;  If it is, divide both registers
  675.               push   di                     ;  by 2 until the high word of the
  676.               mov    di,si                  ;  divisor is zero.
  677.               call   shift_reg
  678.               pop    di
  679.               call   shift_reg
  680.               jmp    short md7
  681. md8:          mov    ax,[di]                ;perform the divide. First the
  682.               cwd                           ;  high word, then the low one.
  683.               idiv   word ptr 2[si]
  684.               mov    bx,ax                  ;Store the high word result in bx
  685.               mov    ax,2[di]
  686.               div    word ptr 2[si]
  687.               pop    cx                     ;get operation back
  688.               cmp    cl,"/"                 ;see if division or mod operator
  689.               je     mdend
  690.               mov    ax,dx                  ;get remainder in in low word and
  691.               xor    bx,bx                  ;  clear the high word.
  692. mdend:        mov    [di],bx
  693.               mov    2[di],ax
  694.               cmp    fixed_flag,0           ;If in fixed mode, divide
  695.               je     mdend1                 ;  by 100 to correct fraction
  696.               cmp    cl,"*"
  697.               jne    mdend1
  698.               mov    ch,1
  699.               call   fixed_adjust
  700. mdend1:       cmp    sign,1                 ;see if odd # of negative numbers
  701.               jne    mdend2                 ;even number, multiply done
  702.               call   negate_reg             ;di already pointing to result reg
  703. mdend2:       xor    cx,cx                  ;clear error flag
  704.               ret
  705. mderr:        pop    bx                     ;clean off stack
  706.               mov    cx,4000h
  707.               ret
  708. mul_div       endp
  709. ;-----------------------------------------------------------------------------
  710. ;Shift register - shifts the register pointed to by di, 1 place.
  711. ;-----------------------------------------------------------------------------
  712. shift_reg     proc near
  713.               push   ax
  714.               push   dx
  715. shift2:       mov    dx,[di]                ;get the register pointed to by bx
  716.               mov    ax,2[di]
  717.               cmp    bl,0                   ;see if left or right shift
  718.               je     shift4                 ;  if bl=0 then left shift
  719. shift3:       sar    dx,1
  720.               rcr    ax,1                   ;Shift 32 bits by shifting the
  721.               jmp    short shift5           ;  trailing word first so that its
  722. shift4:       sal    ax,1                   ;  msb goes into the carry. Then
  723.               rcl    dx,1                   ;  shift the leading word.
  724. shift5:       mov    [di],dx
  725.               mov    2[di],ax
  726. shift_end:    pop    dx
  727.               pop    ax
  728.               ret                           ;done.
  729. shift_reg     endp
  730. ;-----------------------------------------------------------------------------
  731. ;negate register - subtracts a register from zero.
  732. ;entry: di points to the register being negated.
  733. ;-----------------------------------------------------------------------------
  734. negate_reg    proc near
  735.               xor    dx,dx                  ;clear dx
  736. negate2:      neg    word ptr 2[di]         ;negate the register
  737.               sbb    dx,[di]
  738.               mov    [di],dx
  739.               ret
  740. negate_reg    endp
  741. ;-----------------------------------------------------------------------------
  742. ;back_space - remove last digit entered in entry reg by dividing by the base
  743. ;-----------------------------------------------------------------------------
  744. back_space    proc near
  745.               mov    di,offset entry_reg_high
  746.               mov    bx,base_flag
  747.               mov    ch,1
  748.               cmp    fixed_flag,0
  749.               je     back_sp1
  750.               cmp    decimal_flag,3
  751.               je     back_sp1
  752.               call   fixed_adjust
  753.               cmp    decimal_flag,2
  754.               je     back_sp2
  755. back_sp1:     call   mul_div_shrt
  756. back_sp2:     cmp    fixed_flag,0
  757.               je     back_sp5
  758.               mov    bx,100
  759.               cmp    decimal_flag,3
  760.               jne    back_sp3
  761.               mov    bx,10
  762. back_sp3:     xor    cx,cx
  763.               call   mul_div_shrt
  764. back_sp4:     cmp    decimal_flag,0
  765.               je     back_sp5
  766.               dec    decimal_flag
  767. ; display new entry register
  768. back_sp5:     mov    bl,1                   ;display entry register
  769.               call   display_reg            ;call display register
  770. back_end:     ret                           ;done.
  771. back_space    endp
  772. ;-----------------------------------------------------------------------------
  773. ;display_reg - writes a register to the screen at the cursor
  774. ;entry: bl = 0, write result reg. bl = 1, write entry register
  775. ;-----------------------------------------------------------------------------
  776. display_reg   proc near
  777.               mov    cx,36                  ;First erase what was here before
  778.               sub    my_cursor_pos,cx
  779. disp0:        mov    al," "
  780.               call   write_at_cur
  781.               loop   disp0
  782.               cmp    bl,0                   ;which register to display?
  783.               jne    disp1                  ;bl = 0, display result register
  784.               mov    bx,offset result_reg_high
  785.               jmp    short disp2
  786. disp1:        mov    bx,offset entry_reg_high
  787. disp2:        mov    si,2[bx]               ;load register
  788.               mov    di,[bx]
  789.               xor    dx,dx                  ;clear dx
  790.               mov    ch,1                   ;initialize digit counter
  791.               mov    sign,dl                ;clear sign flag
  792.               cmp    base_flag,10           ;is this a decimal mode?
  793.               jne    disp_n6                ;no, skip signed display of number
  794.               cmp    di,0                   ;see if negative number
  795.               jge    disp_n6                ;no, skip negation
  796.               neg    si                     ;yes, negate si,di
  797.               sbb    dx,di
  798.               mov    di,dx
  799.               inc    sign
  800.               jmp    short disp_n6
  801. ;Insert digit seperators.
  802. disp_s1:      inc    ch
  803.               cmp    fixed_flag,0           ;see if in fixed mode
  804.               je     disp_n4
  805.               cmp    ch,3                   ;check for place to put decimal pt
  806.               jne    disp_n30
  807.               mov    al,"."                 ;write decimal point
  808.               jmp    short disp_n51
  809. disp_n30:     mov    al,ch                  ;Since we know were in fixed base,
  810. disp_n31:     mov    cl,3                   ;  set up for groups of 3.
  811.               mov    bl,","                 ;separate decimal digits by a ","
  812.               jmp    short disp_n5
  813. disp_n4:      mov    al,ch                  ;Depending on the base were in,
  814.               dec    al                     ;  set up the number of digits to
  815.               cmp    base_flag,10           ;  count between the separators.
  816.               je     disp_n31               ;  For a base of 8 and 16 separate
  817.               mov    bl," "                 ;  by 4, for binary, separate by 8
  818.               mov    cl,4
  819.               cmp    base_flag,2
  820.               jne    disp_n5
  821.               sal    cl,1
  822. disp_n5:      cbw                           ;Now that everything is set up,
  823.               div    cl                     ;  check if a separator in needed
  824.               cmp    ah,0                   ;  by checking for a remainder of
  825.               jne    disp_n6                ;  zero when dividing the digit
  826.               mov    al,bl                  ;  count by the group number.
  827. disp_n51:     call   write_back
  828. ;Display digit.
  829. disp_n6:      mov    bx,base_flag           ;After all this, its time to
  830.               mov    ax,di                  ;  display the digit. Divide the
  831.               xor    dx,dx                  ;  register to display by the base
  832.               div    bx                     ;  for each of the digits.
  833.               mov    di,ax                  ;save high word quotent
  834.               mov    ax,si
  835.               div    bx
  836.               mov    si,ax                  ;save low word quotent
  837.               mov    ax,dx                  ;get remainder of division
  838.               cmp    al,10                  ;convert remainder to ascii
  839.               jl     disp_n2
  840.               add    al,37h
  841.               jmp    short disp_n3
  842. disp_n2:      or     ax,30h
  843. disp_n3:      call   write_back             ;display digit
  844. ;check to see if we have completed the display.
  845.               cmp    di,0                   ;check for zero number in register
  846.               jne    disp_s1
  847.               cmp    si,0                   ;If the registers are zero, make
  848.               jne    disp_s1                ;  sure that at least 1 zero has
  849.               cmp    fixed_flag,0           ;  been displayed (3 for fixed.)
  850.               je     disp_end               ;  If so, exit loop.
  851.               cmp    ch,3
  852.               jl     disp_s1
  853. disp_end:     cmp    sign,0                 ;If the number displayed was
  854.               je     disp_end1              ;  negative, write a "-" before
  855.               dec    my_cursor_pos          ;  the number.
  856.               mov    al,"-"
  857.               call   write_at_cur
  858. disp_end1:    call   set_cursor
  859.               ret
  860. display_reg   endp
  861. ;This small routine keeps track of the cursor when writing backwards.
  862. write_back    proc   near
  863.               dec    my_cursor_pos
  864.               call   write_at_cur
  865.               dec    my_cursor_pos
  866.               ret
  867. write_back    endp
  868. ;-----------------------------------------------------------------------------
  869. ;Display Base - Show what base the calculator is in.
  870. ;-----------------------------------------------------------------------------
  871. display_base  proc near
  872.               mov    dh,window_row          ;get coordinates of window corner
  873.               mov    dl,window_column
  874.               inc    dh                     ;move down 1 row
  875.               add    dl,20                  ;and over 20 columns
  876.               mov    bx,offset base_ptr     ;load address of base label table
  877.               mov    ax,base_flag           ;find out what base were in
  878.               mov    cl,al                  ;copy base
  879.               sal    cl,1                   ;shift bits over 1
  880.               or     al,cl
  881.               and    al,0ch                 ;look only at the bits we want
  882.               push   ax                     ;save number for later
  883.               sal    ax,1
  884.               cmp    fixed_flag,1           ;see if fixed
  885.               jne    display_b1
  886.               add    al,8                   ;point to fixed label
  887. display_b1:   add    bx,ax
  888.               mov    cx,8
  889.               mov    ah,header_attr
  890. display_b2:   mov    al,[bx]                ;Display label by looping 8 times
  891.               call   output_char
  892.               inc    bx
  893.               inc    dl
  894.               loop   display_b2
  895.               pop    bx                     ;Now display letter to label
  896.               sar    bl,1                   ;  the number currently being
  897.               sar    bl,1                   ;  displayed.
  898.               mov    al,number_label[bx]    ;get letter from list
  899.               mov    ah,text_attr
  900.               mov    dx,my_cursor_pos
  901.               sub    dl,37
  902.               call   output_char
  903.               ret
  904. display_base  endp
  905. ;-----------------------------------------------------------------------------
  906. ;screen ops - saves and restores the contents of the screen beneath the window.
  907. ;Entry:  ES:DI - buffer address,
  908. ;-----------------------------------------------------------------------------
  909. screen_ops    proc near
  910.               push   ds                     ;save es and ds.
  911.               push   es
  912.               push   ax                     ;save entry parameter
  913.               cmp    adapter,0              ;See if CGA, is so disable it
  914.               jne    screen_op1             ;  before writing to the screen.
  915.               call   cga_off
  916. screen_op1:   mov    dh,window_row          ;row and column of window corner
  917.               mov    dl,window_column
  918.               mov    bl,v_page              ;get video page in BX
  919.               mov    si,di                  ;save buffer address
  920.               call   video_ptr              ;get starting address of window
  921.               mov    bx,column_adj          ;load index register adjust
  922.               mov    cx,v_segment           ;get video segment to load into
  923.               pop    ax                     ;  es or ds later.
  924.               cmp    al,1                   ;al=0, screen save. al=1, restore.
  925.               je     screen_op2             ;Since this routine does both the
  926.               assume ds:nothing             ;  screen save and the screen
  927.               assume es:nothing             ;  restore, es:si and ds:di are
  928.               xchg   si,di                  ;  set to their proper addresses.
  929.               mov    ds,cx                  ;  Because of this, make NO
  930.               jmp    short screen_op3       ;  assumptions on es and ds in
  931. screen_op2:   mov    es,cx                  ;  this section of code.
  932. screen_op3:   mov    cx,12                  ;12 lines to save
  933.               cld                           ;clear DF
  934. screen_op4:   push   cx                     ;save line counter
  935.               mov    cx,44                  ;44 words per line
  936.               rep    movsw                  ;transfer one line to buffer
  937.               cmp    al,1
  938.               je     screen_op5
  939.               add    si,bx                  ;adjust si for next line
  940.               jmp    short screen_op6
  941. screen_op5:   add    di,bx                  ;adjust di for next line
  942. screen_op6:   pop    cx                     ;get line count
  943.               loop   screen_op4             ;loop until done
  944.               pop    es                     ;Restore the segment registers
  945.               assume es:code
  946.               pop    ds
  947.               assume ds:code
  948.               cmp    al,0                   ;If we saved the screen, draw the
  949.               jne    screen_op7             ;  calc window into the screen.
  950.               call   open_window
  951. screen_op7:   cmp    adapter,0              ;If CGA, enable it before
  952.               jne    screen_op8             ;  returning.
  953.               call   cga_on
  954. screen_op8:   ret
  955. screen_ops    endp
  956. ;-----------------------------------------------------------------------------
  957. ;Video ptr calculates the offset into the video memory orrsponding to the row,
  958. ;          column, and video page passed to it in the registers given below.
  959. ;entry:  dh,dl - row, column             exit:  di - offset
  960. ;        bl    - video page
  961. ;-----------------------------------------------------------------------------
  962. video_ptr     proc near
  963.               mov    al,160                 ;Compute the displacment by the
  964.               mul    dh                     ;  following equation:
  965.               sal    dl,1                   ;  (row*160)+(col*2)+(page*1000)
  966.               xor    dh,dh
  967.               add    ax,dx
  968.               mov    di,ax
  969.               mov    ax,1000h
  970.               xor    bh,bh
  971.               mul    bx
  972.               add    di,ax
  973.               ret
  974. video_ptr     endp
  975. ;-----------------------------------------------------------------------------
  976. ;cga off routine to disable the CGA by writing to the mode select register
  977. ;-----------------------------------------------------------------------------
  978. cga_off       proc   near
  979.               mov    dx,3DAh                ;Wait for the vertical retrace,
  980. cga_off1:     in     al,dx                  ;  then disable the cga by
  981.               test   al,8                   ;  writing to the mode select
  982.               je     cga_off1               ;  register.
  983.               sub    dx,2
  984.               mov    al,25h
  985.               out    dx,al
  986.               ret
  987. cga_off       endp
  988. ;-----------------------------------------------------------------------------
  989. ;cga on routine to enable the CGA by writing to the mode select register
  990. ;-----------------------------------------------------------------------------
  991. cga_on        proc near
  992.               mov    ah,15                  ;Get the current video mode,
  993.               int    10h                    ;  then, using xlat as a table
  994.               mov    bx,offset enable_values ; lookup get the value needed
  995.               xlat                          ;  to enable the cga video.
  996.               mov    dx,3D8h                ;  Finally, write the value to the
  997.               out    dx,al                  ;  MSR.
  998.               ret
  999. cga_on        endp
  1000. ;-----------------------------------------------------------------------------
  1001. ;KB_RESET resets the keyboard and signals end-of-interrupt to the 8259
  1002. ;-----------------------------------------------------------------------------
  1003. kb_reset      proc near
  1004.               in     al,61h                 ;get current control port value
  1005.               mov    ah,al                  ;save it in AH
  1006.               or     al,80h                 ;set bit 7
  1007.               out    61h,al                 ;send reset value
  1008.               mov    al,ah                  ;get original value
  1009.               out    61h,al                 ;send it out to enable keyboard
  1010.               cli                           ;suspend interrupts
  1011.               mov    al,20h                 ;get EOI value
  1012.               out    20h,al                 ;send EOI to 8259
  1013.               sti                           ;enable interrupts
  1014.               ret
  1015. kb_reset      endp
  1016. ;-----------------------------------------------------------------------------
  1017. ;OUTPUT_CHAR writes the designated character directly to video memory.
  1018. ;Entry:  DH,DL - row, column
  1019. ;        AH,AL - attribute, character
  1020. ;-----------------------------------------------------------------------------
  1021. output_char   proc near
  1022.               push   di
  1023.               push   dx
  1024.               push   es
  1025.               mov    es,v_segment
  1026.               assume es:nothing
  1027.               push   bx
  1028.               push   ax
  1029.               mov    bl,v_page              ;get page in BX
  1030.               call   video_ptr              ;calculate address to write to
  1031.               cmp    adapter,0              ;is this a CGA?
  1032.               jne    output3                ;no, then skip wait loop
  1033.               mov    dx,3DAh                ;get CGA Status Register address
  1034. output1:      in     al,dx                  ;wait until horiz. retrace done
  1035.               test   al,1
  1036.               jne    output1
  1037.               cli                           ;suspend interrupts during write
  1038. output2:      in     al,dx                  ;wait for next horizontal retrace
  1039.               test   al,1
  1040.               je     output2
  1041. output3:      pop    ax                     ;get character and attribute
  1042.               stosw                         ;write them to video memory
  1043.               sti                           ;enable interrupts
  1044.               pop    bx                     ;Restore registers
  1045.               pop    es
  1046.               assume es:code
  1047.               pop    dx
  1048.               pop    di
  1049.               ret
  1050. output_char   endp
  1051. ;-----------------------------------------------------------------------------
  1052. ;OPEN_WINDOW draws the window border onto the screen.  Character/attribute
  1053. ;pairs are sent directly to video memory for fast display speed.
  1054. ;-----------------------------------------------------------------------------
  1055. open_window   proc near
  1056.               mov    dh,window_row          ;get coordinates of window corner
  1057.               mov    dl,window_column
  1058.               push   es
  1059.               mov    es,v_segment           ;point ES to video buffer
  1060.               assume es:nothing
  1061.               cld                           ;clear DF for string operations
  1062.               mov    bl,v_page              ;get video page in BX
  1063.               xor    bh,bh
  1064.               call   video_ptr              ;calculate starting address
  1065. ;Write the top line of the window border to video.
  1066.               mov    al,218                 ;al = left end character
  1067.               mov    ah,border_attr         ;set attribute
  1068.               mov    bl,196                 ;bl = middle 42 characters
  1069.               mov    dl,191                 ;dl = right end character
  1070.               mov    bh,ah                  ;copy the border attribute
  1071.               mov    dh,ah
  1072.               call   write_line
  1073. ;Do the window header line.
  1074.               mov    si,offset header_text  ;point SI to text of line
  1075.               call   write_header
  1076. ;Now write the next 18 lines (no text) to the display.
  1077.               mov    cx,6                   ;6 lines to do
  1078.               mov    al,179                 ;do leftmost column
  1079.               mov    ah,border_attr
  1080.               mov    bl,32                  ;do next 38 columns (blank)
  1081.               mov    bh,text_attr
  1082.               mov    dl,179                 ;do rightmost column
  1083.               mov    dh,ah
  1084. open2:        call   write_line
  1085.               loop   open2                  ;loop until finished
  1086. ;Write line to seperate the active screen from the help text
  1087.               mov    al,195                 ;lower left corner
  1088.               mov    bl,196                 ;line character
  1089.               mov    bh,text_attr
  1090.               mov    dl,180
  1091.               call   write_line
  1092. ;Write the help lines.
  1093.               mov    si,offset help_text1   ;point SI to text of line
  1094.               call   write_header
  1095.               mov    si,offset help_text2   ;point SI to text of line
  1096.               call   write_header
  1097. ;Finish things up by writing the last line.
  1098.               mov    al,192                 ;lower left corner
  1099.               mov    bl,196                 ;line character
  1100.               mov    bh,ah                  ;copy header attribute
  1101.               mov    dl,217
  1102.               call   write_line
  1103.               pop    es
  1104.               assume es:code
  1105.               ret
  1106. open_window   endp
  1107. ; routine to write a line of text to the window
  1108. write_header  proc   near
  1109.               mov    al,179                 ;left window border character
  1110.               stosw                         ;write it
  1111.               mov    cx,42                  ;42 characters to write
  1112.               mov    ah,header_attr         ;use header attribute for these
  1113. open1:        lodsb                         ;get the text character
  1114.               stosw                         ;write char/attr pair to video
  1115.               loop   open1                  ;repeat for all 28
  1116.               mov    ah,border_attr         ;do rightmost column
  1117.               mov    al,179
  1118.               stosw
  1119.               add    di,column_adj          ;adjust DI for next line
  1120.               ret
  1121. write_header  endp
  1122. ;Write a line of characters to the window
  1123. write_line    proc   near
  1124.               push   cx
  1125.               stosw                         ;write left end character
  1126.               mov    cx,42                  ;next 42 characters
  1127.               mov    ax,bx                  ;get middle characters from bx
  1128.               rep    stosw
  1129.               mov    ax,dx                  ;get right end character from dx
  1130.               stosw
  1131.               add    di,column_adj          ;adjust DI for next line
  1132.               pop    cx
  1133.               ret
  1134. write_line    endp
  1135. ;-----------------------------------------------------------------------------
  1136. ;Scroll window up - scroll window up 1 line.
  1137. ;-----------------------------------------------------------------------------
  1138. scroll_window proc   near
  1139.               push   ax                     ;save ax
  1140.               push   cx
  1141.               mov    al," "                 ;First remove cursor by writing
  1142.               mov    ah,text_attr           ;  a space over it.
  1143.               call   write_at_cur
  1144.               mov    ch,window_row          ;put upper left corner in CX
  1145.               add    ch,2
  1146.               mov    cl,window_column
  1147.               inc    cl
  1148.               mov    dx,cx                  ;put lower right corner in DX
  1149.               add    dh,5
  1150.               add    dl,41
  1151.               mov    ah,6h                  ;bios scroll up function
  1152.               mov    al,1                   ;scroll 1 line
  1153.               mov    bh,text_attr           ;fill the line with blanks
  1154.               int    10h                    ;call bios
  1155.               call   set_cursor
  1156.               pop    cx
  1157.               pop    ax
  1158.               ret                           ;done
  1159. scroll_window endp
  1160. set_cursor    proc   near
  1161.               mov    dl,window_column       ;Set up my own cursor location
  1162.               mov    dh,window_row
  1163.               add    dl,38
  1164.               add    dh,7
  1165.               mov    my_cursor_pos,dx
  1166.               ret
  1167. set_cursor    endp
  1168. ;-----------------------------------------------------------------------------
  1169. ;INITIALIZE redirects the keyboard interrupt, then reserves enough memory for
  1170. ;the program to remain resident.
  1171. ;-----------------------------------------------------------------------------
  1172. initialize    proc near
  1173.               mov    dx,offset program      ;Display installation message
  1174.               mov    ah,9
  1175.               int    21h
  1176. ;check for other copies of this program in memory.
  1177.               xor    bx,bx                  ;start search at segment 0
  1178.               mov    word ptr [entry],bx
  1179.               mov    ax,cs                  ;get current segment
  1180. find_loop:    inc    bx                     ;check next segment
  1181.               cmp    ax,bx                  ;did we find ourselves?
  1182.               je     no_copies              ;yes, only 1 copy in memory
  1183.               mov    es,bx                  ;use es as segment pointer
  1184.               assume es:nothing
  1185.               mov    si,offset entry        ;si is the offset pointer
  1186.               mov    di,si                  ;look the same place in both segs
  1187.               mov    cx,16                  ;check 16 bytes
  1188.               cld                           ;incriment pointers during compare
  1189.               repe   cmpsb                  ;compare bytes
  1190.               jne    find_loop              ;if no compare, check another seg
  1191.               mov    dx,offset message1     ;Display other copy found message
  1192.               mov    ah,9
  1193.               int    21h                    ;Terminate without remaining
  1194.               mov    ax,4c01h               ;  resident. Return 1 as rc.
  1195.               int    21h                    ;terminate and stay resident
  1196. no_copies:    mov    ah,35h                 ;get current interrupt 9 vector,
  1197.               mov    al,9                   ;  save it, then replace it with
  1198.               int    21h                    ;  my own.
  1199.               mov    old_keyboard_int,bx
  1200.               mov    old_keyboard_int[2],es
  1201.               mov    ah,25h
  1202.               mov    al,9
  1203.               mov    dx,offset main         ;point it to body of program
  1204.               int    21h
  1205. ;Exit by TSR int 31h. Keep enough memory to store the screen.
  1206.               mov    dx,(offset initialize-offset code+1056+15) shr 4
  1207.               mov    ax,3100h               ;Terminate with 0 return code.
  1208.               int    21h                    ;terminate and stay resident
  1209. initialize    endp
  1210. message1      db     "Calc already present.",13,10,"$"
  1211. code          ends
  1212.               end    entry
  1213.